﻿/* Copyright (c) Microsoft Corporation
 * 
 * All rights reserved.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.  You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
 * 
 * THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABLITY OR NON-INFRINGEMENT.
 * 
 * See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ProximityTapper
{
    /// <summary>
    /// Class for ServiceDescriptorEntryHeader
    /// </summary>
    [CLSCompliant(false)]
    public class ServiceDescriptorEntryHeader : IServiceDescriptorEntryHeader
    {
        private static UInt32 MAX_GUID = 16;
        private static UInt32 SD_HEADER_MIN_SIZE = MAX_GUID + 4 * sizeof(UInt16);

        // Service Activation UUID
        public Guid ServiceActivationGuid { get; private set; }

        // Extended Info 1 (16 bits)
        public UInt16 ExtendedInfo1 { get; private set; }

        // Service Version (16 bits)
        public UInt16 ServiceVersion { get; private set; }

        // Extended Info 2 (16 bits)
        public UInt16 ExtendedInfo2 { get; private set; }

        // Extended Payload byte array 
        public byte[] ExtendedPayload { get; private set; }

        /// <summary>
        /// Try parsing ServiceDescriptorEntryHeader
        /// </summary>
        /// <param name="data">Message Data</param>
        /// <param name="offset">
        /// Starting / current position in byte array.
        /// This is updated as the bytes are read.
        /// </param>
        /// <returns>true is successful in parsing, false otherwise</returns>
        /// <summary>
        bool IServiceDescriptorEntryHeader.TryParse(byte[] data, ref UInt32 offset)
        {
            bool isParsed = true;

            try
            {
                // Verify that remaining data length is atleast minimum size 
                // required by Service Descriptor Entry Header
                if (SD_HEADER_MIN_SIZE <= (data.Length - offset))
                {
                    // Convert byte array to GUID
                    byte[] guid = new byte[MAX_GUID];
                    Array.Copy(data, offset, guid, 0, MAX_GUID);
                    this.ServiceActivationGuid = new Guid(guid);
                    offset += MAX_GUID;

                    this.ExtendedInfo1 = BigEndianConverter.ToUInt16(data, offset);
                    offset += sizeof(UInt16);

                    this.ServiceVersion = BigEndianConverter.ToUInt16(data, offset);
                    offset += sizeof(UInt16);

                    this.ExtendedInfo2 = BigEndianConverter.ToUInt16(data, offset);
                    offset += sizeof(UInt16);

                    // Extract ExtendedPayload
                    UInt16 extendedPayloadCount = BigEndianConverter.ToUInt16(data, offset);
                    offset += sizeof(UInt16);

                    if (extendedPayloadCount > 0)
                    {
                        // Confirm that we have enough bytes to read extended payload
                        if (extendedPayloadCount <= (data.Length - offset))
                        {
                            this.ExtendedPayload = new byte[extendedPayloadCount];
                            Array.Copy(data, offset, guid, 0, extendedPayloadCount);
                            offset += extendedPayloadCount;
                        }
                        else
                        {
                            // Parsing error
                            isParsed = false;
                        }
                    }
                }
                else
                {
                    // Parsing error
                    isParsed = false;
                }
            }
            catch (Exception)
            {
                // Parsing error
                isParsed = false;
            }

            return isParsed;
        }

        public override string ToString()
        {
            String output = "\n\nServiceDescriptorEntryHeader";

            if (this.ServiceActivationGuid != null)
            {
                output += "\nService Activation GUID : " + this.ServiceActivationGuid.ToString();
            }

            output += "\nExtended Info 1 : " + StringFormatter.ToHexString(this.ExtendedInfo1);
            output += "\nService Version : " + StringFormatter.ToHexString(this.ServiceVersion);
            output += "\nExtended Info 2 : " + StringFormatter.ToHexString(this.ExtendedInfo2);

            if (this.ExtendedPayload != null)
            {
                output += "\nExtended Payload Size : " + StringFormatter.ToHexString(this.ExtendedPayload.Length);
                output += "\nExtended Payload : " + StringFormatter.ToHexString(this.ExtendedPayload);
            }
            else
            {
                output += "\nExtended Payload Size : 0x0";
            }

            return output;
        }
    }

    /// <summary>
    /// Class for ServiceDescriptor
    /// </summary>
    [CLSCompliant(false)]
    public class ServiceDescriptor : IServiceDescriptor, IMessageData
    {
        private static UInt32 MAX_ID = 8;

        // Activation Channel Id (8 bytes)
        private byte[] activationChannelId = new byte[MAX_ID];

        public byte[] ActivationChannelId
        {
            get { return activationChannelId; }
            set { activationChannelId = value; }
        }

        // List of Service Descriptor Entry Headers
        private List<IServiceDescriptorEntryHeader> entries = new List<IServiceDescriptorEntryHeader>();

        public List<IServiceDescriptorEntryHeader> Entries
        {
            get { return entries; }
            set { entries = value; }
        }

        /// <summary>
        /// Function to try parse message data
        /// </summary>
        /// <param name="data">Message Data</param>
        /// <returns>true is successful in parsing, false otherwise</returns>
        bool IMessageData.TryParse(byte[] data)
        {
            bool isParsed = true;
            UInt32 offset = 0;

            try
            {
                // Verify that remaining data length is atleast minimum size 
                // required by Service Descriptor Entry Header
                if (MAX_ID <= (data.Length - offset))
                {
                    // Copy Source ID
                    Array.Copy(data, offset, this.ActivationChannelId, 0, MAX_ID);
                    offset += MAX_ID;
                }
                else
                {
                    // Parsing error
                    isParsed = false;
                }
            }
            catch (Exception)
            {
                // Parsing error
                isParsed = false;
            }

            while ((data.Length - offset) > 0)
            {
                IServiceDescriptorEntryHeader iHeader = new ServiceDescriptorEntryHeader();

                if (!iHeader.TryParse(data, ref offset))
                {
                    // Parsing error
                    isParsed = false;
                    break;
                }

                this.Entries.Add(iHeader);
            }

            // Make sure that all bytes are read
            if (offset != data.Length)
            {
                isParsed = false;
            }

            return isParsed;
        }

        public override string ToString()
        {
            String output = "Message Type : Service Descriptor";

            output += "\nActivation Channel Id : " + Convert.ToBase64String(this.ActivationChannelId);

            output += "\nService Descriptor Entry Header Count : " + StringFormatter.ToHexString(this.Entries.Count);

            foreach (IServiceDescriptorEntryHeader header in Entries)
            {
                output += header.ToString();
            }

            return output;
        }
    }
}
